import coin.exchange.base.kr_rest.level_book as level_book_mdl
import coin.exchange.base.kr_rest.public_client_base as pubcb
import coin.exchange.binance.kr_rest.native_public_client as npubc
from coin.exchange.binance.kr_rest.product import BinanceProduct
from coin.exchange.binance.kr_rest.constants import quote_currency_list
from coin.proto.coin_market_query_pb2 import (
    ProductTicker,
    ExchangeTicker,
    ProductKlineElement,
    ProductKline,
)


class BinanceFeedParser(object):
  @staticmethod
  def parse_native_ticker(update_msg, product):
    return ProductTicker(last=float(update_msg['price']),
                         native_symbol=product.native_symbol,
                         symbol=product.symbol)

  @staticmethod
  def parse_exchange_ticker(update_msg):
    product_ticker_list = []
    for ticker in update_msg:
      product = BinanceProduct.FromStrNativeProductNothrow(ticker['symbol'])
      if product is None:
        continue
      product_ticker = ProductTicker(last=float(ticker['price']),
                                     native_symbol=product.native_symbol,
                                     symbol=product.symbol)
      product_ticker_list.append(product_ticker)
    assert len(product_ticker_list) > 0
    return ExchangeTicker(exchange="Binance", each_ticker=product_ticker_list)

  @staticmethod
  def parse_level_book(update_msg, product):
    level_book = level_book_mdl.LevelBook(product)
    for elem in update_msg['asks']:
      price, qty, _ = elem
      level_elem = level_book_mdl.LevelElement(float(price), float(qty), 0)
      level_book.add_ask(level_elem)
    for elem in update_msg['bids']:
      price, qty, _ = elem
      level_elem = level_book_mdl.LevelElement(float(price), float(qty), _)
      level_book.add_bid(level_elem)
    level_book.finalize()
    return level_book

  @staticmethod
  def parse_native_kline(update_msg, product, kline_period):
    """Sample kline response:
    [
      [
        1499040000000,      // Open time
        "0.01634790",       // Open
        "0.80000000",       // High
        "0.01575800",       // Low
        "0.01577100",       // Close
        "148976.11427815",  // Volume
        1499644799999,      // Close time
        "2434.19055334",    // Quote asset volume
        308,                // Number of trades
        "1756.87402397",    // Taker buy base asset volume
        "28.46694368",      // Taker buy quote asset volume
        "17928899.62484339" // Ignore.
      ]
    ]
    """
    klines = update_msg
    kline_group = []
    for kline in klines:
      volume = float(kline[5])
      buy_volume = float(kline[9])
      sell_volume = volume - buy_volume
      product_kline = ProductKlineElement(kline_timestamp=int(kline[0] * 1e+6),
                                          open=float(kline[1]),
                                          high=float(kline[2]),
                                          low=float(kline[3]),
                                          close=float(kline[4]),
                                          volume=volume,
                                          buy_volume=buy_volume,
                                          sell_volume=sell_volume,
                                          turnover=float(kline[7]))
      kline_group.append(product_kline)
    return ProductKline(
        symbol=product.symbol,
        native_symbol=product.native_symbol,
        exchange='Binance',
        market_type='Spot',
        kline_period=kline_period,
        klines=kline_group,
    )


class BinancePublicClient(pubcb.PublicClientBase):
  ProductType = BinanceProduct

  def __init__(self):
    self.npubc = npubc.BinanceNativePublicClient()

  def query_ticker_impl(self, product):
    symbol = product.native_symbol
    update = self.npubc.get_ticker(symbol)
    update.msg = BinanceFeedParser.parse_native_ticker(update.msg, product)
    return update

  def query_exchange_ticker_impl(self):
    update = self.npubc.get_ticker()
    update.msg = BinanceFeedParser.parse_exchange_ticker(update.msg)
    return update

  def query_history_kline_impl(self, product, kline_period, start_time, end_time):
    symbol = product.native_symbol
    update = self.npubc.get_history_kline(symbol, kline_period, start_time, end_time)
    update.msg = BinanceFeedParser.parse_native_kline(update.msg, product, kline_period)
    return update

  def query_level_book_impl(self, product):
    symbol = product.native_symbol
    update = self.npubc.get_orderbook(symbol)
    update.msg = BinanceFeedParser.parse_level_book(update.msg, product)
    return update
